package hh.util.time;


import hh.util.Asserts;
import hh.util.CommonUtil;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author huanghuai
 * @date 2020/9/14
 */
@SuppressWarnings("all")
public class TimeUtil implements Serializable {
    private static final long serialVersionUID = 4556597632923289101L;

    public static final TimeZone timeZone = TimeZone.getDefault();
    public static final String YYYY = "yyyy";
    public static final String YYYY_MM_DD = "yyyyMMdd";
    public static final String YYYY_MM_DD_ = "yyyy-MM-dd";
    public static final String YY_MM_DD_ = "yy-MM-dd";
    public static final String YY_MM_DD = "yyMMdd";
    public static final String YYYY_MM_ = "yyyy-MM";
    public static final String YYYY_MM_DD_HH_MM_SS_ = "yyyy-MM-dd HH:mm:ss";
    public static final String YYYY_MM_DD_HH_MM_SS_SSS_ = "yyyy-MM-dd HH:mm:ss.SSS";

    public static final String YYYY_MM_DD_HH_MM_SS_SSS_WITH_ZONE = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; //2001-07-04T12:08:56.235-0700

    public static final String US_LOCALE1 = "EEEE MMM dd HH:mm:ss zzz yyyy"; //Mon Dec 14 15:54:08 CST 2020 星期一 十二月 14 15:54:08 CST 2020


    public static final long SECOND_MS = 1000;
    public static final long MINUTE_MS = SECOND_MS * 60;
    public static final long HOUR_MS = MINUTE_MS * 60;
    public static final long DAY_MS = HOUR_MS * 24;
    public static final long WEEK_MS = DAY_MS * 7;
    public static final long YEAR_MS = DAY_MS * 365;
    /**
     * 闰年天数
     */
    public static final int LEAP_YEAR_DAYS = 366;
    /**
     * 闰年毫秒数
     */
    public static final long LEAP_YEAR_DAYS_MS = LEAP_YEAR_DAYS * DAY_MS;
    /**
     * 平年天数
     */
    public static final int ORDINARY_YEAR_DAYS = 365;
    /**
     * 平年毫秒数
     */
    public static final long ORDINARY_YEAR_DAYS_MS = ORDINARY_YEAR_DAYS * DAY_MS;

    // 月份不表示，因为有28-31天，不准确
    public static final String[] cnUnitName = {"年", "周", "天", "小时", "分钟", "秒", "毫秒"};
    public static final String[] enUnitName = {"year", "week", "day", "hour", "min", "s", "ms"};

    private TimeUtil() {
    }

    public static Date quickParseDate(long time) {
        return null;
    }

    /**
     * return Takes[16min 40s 6ms]
     */
    public static String countTimeEn(long start) {
        return "Takes[" + toStr(System.currentTimeMillis() - start, enUnitName, " ") + "]";
    }

    public static String countTimeEnMs(long start) {
        long ms = System.currentTimeMillis() - start;
        return "Takes[" + toStr(ms, enUnitName, " ") + "(+" + ms + "ms)]";
    }

    /**
     * return Takes[16min 40s 6ms]
     */
    public static String countTimeEn(long start, long end) {
        return "Takes[" + toStr(end - start, enUnitName, " ") + "]";
    }

    public static String countTimeEnMs(long start, long end) {
        long ms = end - start;
        return "Takes[" + toStr(ms, enUnitName, " ") + "(+" + ms + "ms)]";
    }

    /**
     * return 16min 40s 6ms
     */
    public static String toEn(long ms) {
        return toStr(ms, enUnitName, " ");
    }

    /**
     * return 耗时:16分钟40秒6毫秒
     */
    public static String countTimeCn(long start) {
        return "耗时:" + toStr(System.currentTimeMillis() - start, cnUnitName, "");
    }

    public static String countTimeCnMs(long start) {
        long ms = System.currentTimeMillis() - start;
        return "耗时:" + toStr(ms, cnUnitName, "") + "(" + ms + "ms)";
    }

    /**
     * return 耗时:16分钟40秒6毫秒
     */
    public static String countTimeCn(long start, long end) {
        return "耗时:" + toStr(end - start, cnUnitName, "");
    }

    public static String countTimeCnMs(long start, long end) {
        long ms = end - start;
        return "耗时:" + toStr(ms, cnUnitName, "") + "(" + ms + "ms)";
    }

    /**
     * return 16分钟40秒6毫秒
     */
    public static String toCn(long ms) {
        return toStr(ms, cnUnitName, "");
    }


    private static String toStr(long ms, String[] unitNames, String space, TimeUnit unit) {
        if (ms == 0) {
            return "0";
        }
        if (unit != null) {
            long l = unit.toMillis(1);
            if (l != 0)
                ms = ms - (ms % l);
        }
        StringBuilder sb = new StringBuilder(16);
        long[] timeUnitArr = getTimeUnitArr(ms);
        final int length = timeUnitArr.length;
        // 周转移为天 因为中国不习惯用week表示，比如 1年5周10天，一般替换为  1年45天
        timeUnitArr[2] = timeUnitArr[2] + timeUnitArr[1] * 7;
        timeUnitArr[1] = 0;
        for (int i = 0; i < length; i++) {
            if (timeUnitArr[i] != 0) {
                sb.append(timeUnitArr[i]).append(unitNames[i]);
                if (i < length - 1) {
                    sb.append(space);
                }
            }
        }
        return sb.toString();
    }

    private static String toStr(long ms, String[] unitNames, String space) {
        return toStr(ms, unitNames, space, null);
    }

    private static long[] getTimeUnitArr(long time) {
        return new long[]{
                time / YEAR_MS,
                (time = time % YEAR_MS) / WEEK_MS,
                (time = time % WEEK_MS) / DAY_MS,
                (time = time % DAY_MS) / HOUR_MS,
                (time = time % HOUR_MS) / MINUTE_MS,
                (time = time % MINUTE_MS) / SECOND_MS,
                time % SECOND_MS};
    }


    /**
     * 是否闰年 <br>
     * 四年一闰,百年不闰,四百年再闰 <br>
     * 1.能被4整除而不能被100整除。 2.能被100整除也能被400整除
     */
    public static boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0)
                || (year % 100 == 0 && year % 400 == 0);
    }

    /**
     * 世纪闰年
     */
    public static boolean isCenturyLeapYear(int year) {
        return (year % 100 == 0 && year % 400 == 0);
    }


    public static Date parseDate(String dataString, String pattern) {
        try {
            return new SimpleDateFormat(pattern).parse(dataString);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static String formatShort(long timestamp) {
        return formatShort(new Date(timestamp));
    }

    public static String formatShort(Date date) {
        try {
            return new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS_).format(date);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static String formatLong(long timestamp) {
        return formatLong(new Date(timestamp));
    }
    public static String formatLong() {
        return formatLong(new Date());
    }

    public static String formatLong(Date date) {
        try {
            return new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS_SSS_).format(date);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static long getDays(int year) {
        return isLeapYear(year) ? LEAP_YEAR_DAYS : ORDINARY_YEAR_DAYS;
    }

    private static final String[] usefulPattens = {YYYY_MM_DD_HH_MM_SS_SSS_, YYYY_MM_DD_HH_MM_SS_,
            YYYY_MM_DD_HH_MM_SS_SSS_WITH_ZONE, US_LOCALE1, US_LOCALE1};

    public static Date parseDate(String dateStr) {
        Locale china = Locale.CHINA;
        for (int i = 0; i < 5; i++) {
            try {
                if (i == 4) return new SimpleDateFormat(usefulPattens[i], Locale.US).parse(dateStr);
                return new SimpleDateFormat(usefulPattens[i], china).parse(dateStr);
            } catch (Exception e) {
            }
        }
        throw new IllegalArgumentException("无法将" + dateStr + "解析成Date");
    }

    static Map<String, TimeUnit> millisecondUnitMap = (Map<String, TimeUnit>) CommonUtil.newTreeMapKvs(
            "ms", TimeUnit.MILLISECONDS,
            "millis", TimeUnit.MILLISECONDS,
            "millisecond", TimeUnit.MILLISECONDS,
            "milliseconds", TimeUnit.MILLISECONDS
    );
    static Map<String, TimeUnit> secondUnitMap = (Map<String, TimeUnit>) CommonUtil.newTreeMapKvs(
            "s", TimeUnit.SECONDS,
            "sec", TimeUnit.SECONDS,
            "second", TimeUnit.SECONDS,
            "seconds", TimeUnit.SECONDS
    );
    static Map<String, TimeUnit> minuteUnitMap = (Map<String, TimeUnit>) CommonUtil.newTreeMapKvs(
            "m", TimeUnit.MINUTES,
            "min", TimeUnit.MINUTES,
            "minute", TimeUnit.MINUTES,
            "minutes", TimeUnit.MINUTES
    );
    static Map<String, TimeUnit> hourUnitMap = (Map<String, TimeUnit>) CommonUtil.newTreeMapKvs(
            "h", TimeUnit.HOURS,
            "hour", TimeUnit.HOURS,
            "hours", TimeUnit.HOURS
    );
    static Map<String, TimeUnit> dayUnitMap = (Map<String, TimeUnit>) CommonUtil.newTreeMapKvs(
            "d", TimeUnit.DAYS,
            "day", TimeUnit.DAYS,
            "days", TimeUnit.DAYS
    );


    static Map<String, TimeUnit> unitMap = new TreeMap<String, TimeUnit>() {{
        putAll(millisecondUnitMap);
        putAll(secondUnitMap);
        putAll(minuteUnitMap);
        putAll(hourUnitMap);
        putAll(dayUnitMap);
    }};

    public static final String unitString;

    static {
        StringBuilder sb = new StringBuilder(130);
        sb.append("\n毫秒=").append(millisecondUnitMap.keySet().stream().collect(Collectors.joining(","))).append("\n");
        sb.append("秒=").append(secondUnitMap.keySet().stream().collect(Collectors.joining(","))).append("\n");
        sb.append("分钟=").append(minuteUnitMap.keySet().stream().collect(Collectors.joining(","))).append("\n");
        sb.append("小时=").append(hourUnitMap.keySet().stream().collect(Collectors.joining(","))).append("\n");
        sb.append("天=").append(dayUnitMap.keySet().stream().collect(Collectors.joining(",")));
        unitString = sb.toString();
    }

    /**
     * m min minute minutes
     * s second sec
     * ms millis millisecond
     * h hour
     * d day
     */
    public static List<Tunit> parseTimeUnits(String times) {
        Asserts.empty(times, "times参数不能为空");
        String[] split = times.split("[,;]");
        List<Tunit> tunits = new ArrayList<>();
        String num = "";
        String unit = "";
        for (String t : split) {
            t = t.trim().toLowerCase(Locale.ROOT);
            for (int i = 0; i < t.length(); i++) {
                if (!Character.isDigit(t.charAt(i))) {
                    num = t.substring(0, i);
                    unit = t.substring(i).trim();
                    TimeUnit u = unitMap.get(unit);

                    Asserts.Null(u, "时间单位[{}]不存在,正确单位：{}", t, unitString);
                    Asserts.True(num.isEmpty(), "请给出数字时间：" + t);
                    tunits.add(new Tunit(Integer.parseInt(num), u));
                    break;
                }
            }

            if (unit.isEmpty()) {
                throw new RuntimeException("请给出时间单位：" + t);
            }
        }
        return tunits;
    }

    public static Tunit parseTimeUnit(String time) {
        return parseTimeUnits(time).get(0);
    }

    public static void main(String[] args) throws Exception {
        System.out.println(parseTimeUnits(""));
    }
}
